﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using ClearingRestartingAutomaton.Base;
using ClearingRestartingAutomaton.Common;

namespace ClearingRestartingAutomaton
{
    public partial class InferenceForm : Form
    {
        public InferenceForm()
        {
            InitializeComponent();
            this.assumptionsInference = new AssumptionsInference();
            this.automatonInference = new AutomatonInference();
            this.assumptionsComboBox.SelectedIndex = 0;
        }

        private string leftContext;
        private string rightContext;
        private string[] positiveSampleArray;
        private string[] negativeSampleArray;
        private int maximalWidth;
        private int contextLength;
        private bool enableDeltaRules;
        private AssumptionsInference assumptionsInference;
        private AutomatonInference automatonInference;

        private bool ReadInitialConditions(bool showMessages)
        {
            this.maximalWidth = (int)this.maximalWidthNumericUpDown.Value;
            this.contextLength = (int)this.contextLengthNumericUpDown.Value;

            if (RegexPatterns.LeftContextRegex.IsMatch(leftContextTextBox.Text))
            {
                this.leftContext = leftContextTextBox.Text.Replace(".", "").Trim();
            }
            else
            {
                if (showMessages)
                {
                    MessageBox.Show("Left context does not match the pattern.", "Warning");
                }
                return false;
            }

            if (RegexPatterns.RightContextRegex.IsMatch(rightContextTextBox.Text))
            {
                this.rightContext = rightContextTextBox.Text.Replace(".", "").Trim();
            }
            else
            {
                if (showMessages)
                {
                    MessageBox.Show("Right context does not match the pattern.", "Warning");
                }
                return false;
            }

            this.positiveSampleArray = this.positiveSamplesRichTextBox.Text.Split(
                new char[] { ' ', '\t', '\r', '\n', ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
            if (positiveSampleArray.Length == 0)
            {
                if (showMessages)
                {
                    MessageBox.Show("There are no valid positive samples.", "Warning");
                }
                return false;
            }

            this.negativeSampleArray = this.negativeSamplesRichTextBox.Text.Split(
                new char[] { ' ', '\t', '\r', '\n', ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
            if (negativeSampleArray.Length == 0)
            {
                if (showMessages)
                {
                    MessageBox.Show("There are no valid negative samples.", "Warning");
                }
                return false;
            }

            return true;
        }

        private void DebugClear()
        {
            this.debugRichTextBox.Clear();
        }

        private void DebugLogAppend(string text)
        {
            this.debugRichTextBox.AppendText(text);
        }

        private void DebugLogAppendLine()
        {
            this.DebugLogAppend(Environment.NewLine);
        }

        private void DebugLogAppendLine(string text)
        {
            this.DebugLogAppend(text + Environment.NewLine);
        }

        private void inferButton_Click(object sender, EventArgs e)
        {
            this.DebugClear();

            if (this.ReadInitialConditions(true))
            {
                this.DebugLogAppend("POSITIVE SAMPLES: ");
                this.DebugLogAppendLine(String.Join(", ", this.positiveSampleArray));
                this.DebugLogAppend("NEGATIVE SAMPLES: ");
                this.DebugLogAppendLine(String.Join(", ", this.negativeSampleArray));
                this.DebugLogAppendLine();

                if (this.assumptionsComboBox.SelectedIndex >= 0 && this.assumptionsComboBox.SelectedIndex < 4)
                {
                    this.assumptionsInference.Log = DebugLogAppend;
                    this.assumptionsInference.PositiveSamples.Clear();
                    this.assumptionsInference.NegativeSamples.Clear();
                    this.assumptionsInference.PositiveSamples.AddRange(this.positiveSampleArray);
                    this.assumptionsInference.NegativeSamples.AddRange(this.negativeSampleArray);

                    Assumptions assumptions = null;
                    switch (this.assumptionsComboBox.SelectedIndex)
                    {
                        case 0:
                            this.DebugLogAppend("ASSUMPTIONS PROCEDURE: ClearingAssumptionsWeak: ");
                            this.DebugLogAppendLine(
                                "Whenever two patterns: xzy and xy occur (as subwords) in the set of positive " +
                                "samples, the instruction (x, z, y) is added to the resulting collection of instructions, " +
                                "provided that it is a correct instruction of a k-clearing restarting automaton " +
                                "having the specified maximal width.");
                            assumptions = AssumptionsMethods.ClearingAssumptionsWeak; 
                            break;
                        case 1:
                            this.DebugLogAppend("ASSUMPTIONS PROCEDURE: ClearingAssumptionsStrong: ");
                            this.DebugLogAppendLine(
                                "The instruction (x, z, y) is included in the resulting collection of instructions " +
                                "if and only if there are two positive samples w_1 and w_2, such that w_1 can be reduced " +
                                "to w_2 by using this instruction, and it is a correct instruction of a k-clearing restarting " + 
                                "automaton having the specified maximal width.");
                            assumptions = AssumptionsMethods.ClearingAssumptionsStrong; 
                            break;
                        case 2:
                            this.DebugLogAppend("ASSUMPTIONS PROCEDURE: SubwordAssumptionsWeak: ");
                            this.DebugLogAppendLine(
                                "Whenever two patterns: xzy and xty occur (as subwords) in the set of positive " +
                                "samples, where t is a proper subword of z, the instruction (x, z -> t, y) is added " +
                                "to the resulting collection of instructions, provided that it is a correct instruction " +
                                "of a k-subword clearing restarting automaton having the specified maximal width.");
                            assumptions = AssumptionsMethods.SubwordAssumptionsWeak; 
                            break;
                        case 3:
                            this.DebugLogAppend("ASSUMPTIONS PROCEDURE: SubwordAssumptionsWeak: ");
                            this.DebugLogAppendLine(
                                "The instruction (x, z -> t, y) is included in the resulting collection of instructions " +
                                "if and only if t is a proper subword of z and there are two positive samples w_1 and w_2, " +
                                "such that w_1 can be reduced to w_2 by using this instruction, and it is a correct instruction " +
                                "of a k-subword clearing restarting automaton having the specified maximal width.");
                            assumptions = AssumptionsMethods.SubwordAssumptionsStrong; 
                            break;
                    }

                    this.DebugLogAppendLine();

                    Automaton automaton = this.assumptionsInference.GetAutomaton(assumptions, this.maximalWidth, this.contextLength);

                    if (automaton != null)
                    {
                        InformationForm infoForm = new InformationForm(automaton.ToString());
                        infoForm.Show();
                    }
                }
                else if (this.assumptionsComboBox.SelectedIndex >= 4 && this.assumptionsComboBox.SelectedIndex < 6)
                {
                    if (!this.backgroundWorker.IsBusy)
                    {
                        this.inferButton.Enabled = false;
                        this.cancelButton.Enabled = true;

                        this.automatonInference.PositiveSamples.Clear();
                        this.automatonInference.NegativeSamples.Clear();
                        this.automatonInference.PositiveSamples.AddRange(this.positiveSampleArray);
                        this.automatonInference.NegativeSamples.AddRange(this.negativeSampleArray);

                        switch (this.assumptionsComboBox.SelectedIndex)
                        {
                            case 4:
                                this.DebugLogAppendLine("SELECTED MODEL: Clearing Restarting Automaton");
                                this.enableDeltaRules = false;
                                break;
                            case 5:
                                this.DebugLogAppendLine("SELECTED MODEL: Delta-Clearing Restarting Automaton");
                                this.enableDeltaRules = true;
                                break;
                        }

                        DebugLogAppendLine("SEARCH ALGORITHM STARTED AT " + DateTime.Now.ToString());
                        DebugLogAppendLine("PROHIBITED INSTRUCTIONS: ");

                        this.backgroundWorker.RunWorkerAsync();
                    }
                }
            }
        }

        private void cancelButton_Click(object sender, EventArgs e)
        {
            if (this.backgroundWorker.WorkerSupportsCancellation)
            {
                this.backgroundWorker.CancelAsync();
            }
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            e.Result = this.automatonInference.GetAutomaton(this.maximalWidth, this.contextLength, 0, this.enableDeltaRules, worker);

            if (worker.CancellationPending)
            {
                e.Cancel = true;
            }
        }

        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState != null && e.UserState is string)
            {
                DebugLogAppendLine("{ " + e.UserState as string + " }");
            }
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                DebugLogAppendLine("INFERENCE ALGORITHM CANCELLED");
            }
            else if (e.Error != null)
            {
                DebugLogAppendLine("EXCEPTION: " + e.Error.Message);
            }
            else
            {
                DebugLogAppendLine("SEARCH ALGORITHM FINISHED AT " + DateTime.Now.ToString());
                Automaton automaton = e.Result as Automaton;
                if (automaton != null)
                {
                    DebugLogAppendLine("THE RESULTING AUTOMATON IS CONSISTENT");
                    DebugLogAppend(automaton.ToString());

                    InformationForm infoForm = new InformationForm(automaton.ToString());
                    infoForm.Show();
                }
                else
                {
                    DebugLogAppendLine("COULD NOT FIND ANY CONSISTENT AUTOMATON");
                }
            }
            this.inferButton.Enabled = true;
            this.cancelButton.Enabled = false;
        }

        private void clearPositiveSamplesButton_Click(object sender, EventArgs e)
        {
            this.positiveSamplesRichTextBox.Clear();
        }

        private void copyPositiveSamplesButton_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrWhiteSpace(this.positiveSamplesRichTextBox.Text))
            {
                Clipboard.SetText(this.positiveSamplesRichTextBox.Text);
            }
        }

        private void pastePositiveSamplesButton_Click(object sender, EventArgs e)
        {
            if (Clipboard.ContainsText())
            {
                this.positiveSamplesRichTextBox.Clear();
                this.positiveSamplesRichTextBox.AppendText(Clipboard.GetText());
            }
        }

        private void clearNegativeSamplesButton_Click(object sender, EventArgs e)
        {
            this.negativeSamplesRichTextBox.Clear();
        }

        private void copyNegativeSamplesButton_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrWhiteSpace(this.negativeSamplesRichTextBox.Text))
            {
                Clipboard.SetText(this.negativeSamplesRichTextBox.Text);
            }
        }

        private void pasteNegativeSamplesButton_Click(object sender, EventArgs e)
        {
            if (Clipboard.ContainsText())
            {
                this.negativeSamplesRichTextBox.Clear();
                this.negativeSamplesRichTextBox.AppendText(Clipboard.GetText());
            }
        }
    }
}
